package net.csdn.business.notify.api.service.impl;

import lombok.extern.slf4j.Slf4j;
import net.csdn.business.notify.api.service.*;
import net.csdn.business.notify.common.config.AsyncThreadPoolConfig;
import net.csdn.business.notify.common.config.TargetTypeConfig;
import net.csdn.business.notify.common.config.properties.NotifyLimitProperties;
import net.csdn.business.notify.common.constant.RedisConstants;
import net.csdn.business.notify.common.enums.ProcessTypeEnum;
import net.csdn.business.notify.common.enums.ResultCodeEnum;
import net.csdn.business.notify.common.exception.BusinessException;
import net.csdn.business.notify.common.model.dto.MarkMessageDTO;
import net.csdn.business.notify.common.model.dto.UserSendPrivateEnableDTO;
import net.csdn.business.notify.common.model.dto.UserSendPrivateMsgDTO;
import net.csdn.business.notify.common.model.vo.NotifyTargetVO;
import net.csdn.business.notify.common.model.vo.UserUnReadConutVO;
import net.csdn.business.notify.common.pipeline.param.SendPrivateParam;
import net.csdn.business.notify.common.redis.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.Future;

/**
 * @ClassName NotifyServiceImpl
 * @Description TODO
 * @Author zhangyl
 * @Date 2023/12/7 16:48
 */
@Slf4j
@Service
public class NotifyServiceImpl implements INotifyService {

    @Autowired
    private TargetTypeConfig targetTypeConfig;
    @Autowired
    private NotifyLimitProperties notifyLimitProperties;

    @Autowired
    private ISendService sendService;

    @Autowired
    private INotifyPrivateLetterService privateLetterService;

    @Autowired
    private INotifyRemindRecordService remindRecordService;

    @Autowired
    private INotifyAnnounceService announceService;
    @Autowired
    private INotifyUserSettingService notifyUserSettingService;




    @Resource(name= AsyncThreadPoolConfig.ASYNC_EXECUTOR_POOL_NAME)
    private ThreadPoolTaskExecutor asyncExecutor;


    @Autowired
    private RedisUtils redisUtils;


    private DefaultRedisScript<Long> redisScript;


    @PostConstruct
    public void init() {
        redisScript = new DefaultRedisScript();
        redisScript.setResultType(Long.class);
        redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua")));
    }

    @Override
    public List<NotifyTargetVO> getTargetInfo() {
        return targetTypeConfig.getList();
    }

    @Override
    public UserUnReadConutVO getUnReadCount(String currUserId) {
        UserUnReadConutVO result=new UserUnReadConutVO();
        try {
            //查询私信未读的数量
            Future<Integer> taskA =asyncExecutor.submit(()->{
                return privateLetterService.getUnReadCount(currUserId);
            });
            //查询系统通知未读的数量
            Future<Integer> taskB =asyncExecutor.submit(()->{
                return remindRecordService.getUnReadCount(currUserId);
            });
            //查询公告未读的数量
            Future<Integer> taskC =asyncExecutor.submit(()->{
                return announceService.getUnReadCount(currUserId);
            });
            result.setPrivateCount(taskA.get());
            result.setRemindCount(taskB.get());
            result.setAnnounceCount(taskC.get());
        }catch (Exception e){
            e.printStackTrace();
            log.error("query notify user:{}  unread msg count error",currUserId);
        }
        return result;
    }

    /**
     * @Author zhangyl
     * @Description 用户发送私信功能
     * @Date 15:13 2023/12/13
     * @Param
     * @return
     **/
    @Override
    public Integer userSendPrivateMsg(UserSendPrivateMsgDTO params) throws Exception{
        String currUserId=params.getCurrUserId();
        String receiverId=params.getReceiverId();
        //自己不能给自己发送私信限制
        if(receiverId.equals(currUserId)){
            throw new BusinessException(ResultCodeEnum.NOTIFY_SEND_MSG_TO_SELF_ERROR);
        }
        String batchNum=params.getBatchNum();
        //查询是该用户否限流，采用滑动时间窗口限流算法
        long nowTime = System.currentTimeMillis();
        String key = RedisConstants.USER_PRIVATE_MESSAGE+currUserId;
        String score = String.valueOf(nowTime);
        if (redisUtils.execLimitLua(redisScript, Collections.singletonList(key), String.valueOf(notifyLimitProperties.getPrivateWindowTime() * 1000), score, String.valueOf(notifyLimitProperties.getPrivateNum()), batchNum)) {
            throw new BusinessException(ResultCodeEnum.NOTIFY_SEND_MSG_EXCEED_LIMIT_ERROR);
        }
        //查询用户是否可以发送私信，如果不可以，则返回错误
        UserSendPrivateEnableDTO senEnableCheckparams=new UserSendPrivateEnableDTO();
        senEnableCheckparams.setCurrUserId(currUserId);
        senEnableCheckparams.setReceiverId(receiverId);
        boolean isEnableSend=notifyUserSettingService.userSendPrivateEnable(senEnableCheckparams);
        if(!isEnableSend){
            throw new BusinessException(ResultCodeEnum.NOTIFY_NOT_ENABLE_SEND_MSG_ERROR);
        }

        SendPrivateParam sendPrivateParam=new SendPrivateParam();
        sendPrivateParam.setContent(params.getContent());
        sendPrivateParam.setSender(params.getCurrUserId());
        Set<String> receivers=new HashSet<>();
        receivers.add(params.getReceiverId());
        sendPrivateParam.setReceiver(receivers);
        sendPrivateParam.setBatchNum(params.getBatchNum());
        sendService.sendPrivate(ProcessTypeEnum.SEND_PRIVATE_NOTIFY.getCode(),sendPrivateParam);
        return 1;
    }

    @Override
    public void markNotify(MarkMessageDTO params) {
        Integer scope=params.getScope();
        String  currUserId=params.getCurrUserId();
        int res=0;
        switch (scope){
            case 1:
                //私信一键已读
                res=privateLetterService.markRead(currUserId);
                log.debug("update private unread msg total:{}",res);
                break;
            case 2:
                //通知一键已读
                res=remindRecordService.markRead(currUserId);
                log.debug("update remind unread msg total:{}",res);
                break;
        }
    }
}
